﻿using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl;

using Weazel.Willows.Model;

namespace Weazel.Willows.InterfaceGeneration
{
	public class IpifImplementation
	{
		private Model.Model model;
		private MemoryMap memoryMap = new MemoryMap();


		private HardwareQueueRegistry queueRegistry = new HardwareQueueRegistry();

		private IpifTopLevelEntity ipifTopLevelEntity;

		private Weazel.Badger.ConversionEntity hardwareEntity;

		private UserLogicDesignFile userLogicDesignFile;

		public void Generate(
			List<Queue> hwToSwConnections,
			List<Queue> swToHwConnections
		)
		{
			// total number of memory addresses needed
			int totalAddresses = 0;

			StatusRegisterSoftwareToHardware swHwStatusRegister =
				new StatusRegisterSoftwareToHardware(memoryMap);

			foreach (Queue queue in swToHwConnections)
			{
				SoftwareToHardwareQueue swHwConnection =
					new SoftwareToHardwareQueue(memoryMap, queueRegistry, queue);

				swHwConnection.AssignMemoryAddress(totalAddresses, swHwConnection.AddressesNeeded);

				// increment total number of addresses needed
				totalAddresses += swHwConnection.AddressesNeeded;

				swHwStatusRegister.Add(swHwConnection);
			}

			StatusRegisterHardwareToSoftware hwSwStatusRegister =
				new StatusRegisterHardwareToSoftware(memoryMap);

			foreach (Queue queue in hwToSwConnections)
			{
				HardwareToSoftwareQueue hwSwConnection =
					new HardwareToSoftwareQueue(memoryMap, queueRegistry, queue);

				hwSwConnection.AssignMemoryAddress(totalAddresses, hwSwConnection.AddressesNeeded);

				// increment totalt number of addresses needed
				totalAddresses += hwSwConnection.AddressesNeeded;

				hwSwStatusRegister.Add(hwSwConnection);
			}



			hwSwStatusRegister.AssignMemoryAddress(totalAddresses, hwSwStatusRegister.AddressesNeeded);
			totalAddresses += hwSwStatusRegister.AddressesNeeded;

			swHwStatusRegister.AssignMemoryAddress(totalAddresses, swHwStatusRegister.AddressesNeeded);
			totalAddresses += swHwStatusRegister.AddressesNeeded;

			Console.WriteLine(memoryMap);


			UserLogicEntity e = new UserLogicEntity(memoryMap, hardwareEntity);
			userLogicDesignFile = new UserLogicDesignFile(e);

			ipifTopLevelEntity = new IpifTopLevelEntity(memoryMap);
		}

		private void writeEntityToFile(ref List<string> files, Weazel.Badger.Vhdl.Entity entity, string outputDirectory)
		{
			Console.WriteLine("Writing entity: " + entity.Name);

			DesignFile designFile = new DesignFile(entity.Name);
			designFile.Entities.Add(entity);
			designFile.DesignUnit.Add("library ieee;");
			designFile.DesignUnit.Add("use IEEE.std_logic_1164.all;");
			designFile.DesignUnit.Add("use IEEE.std_logic_arith.all;");
			designFile.DesignUnit.Add("use std.textio.all;");
			designFile.DesignUnit.Add("use work.std_logic_arithext.all;");

			designFile.Write(outputDirectory);

			files.Add(designFile.FileName);

			foreach (Weazel.Badger.Vhdl.ComponentDeclaration child in entity.Children.Values)
				if(child.Entity != null)
					writeEntityToFile(ref files, child.Entity, outputDirectory);
		}

		public void Write(ImplementationConfiguration configuration)
		{
			// write the top level entity
			ipifTopLevelEntity.WriteToFile(configuration.HardwareImplementationDirectory + "//weazel.vhd");

			// write queue implementation(s)
			queueRegistry.WriteToDisk(configuration.HardwareImplementationDirectory);			

			// write the 'std_logic_arithext' file
			Weazel.Badger.Vhdl.LibraryFiles.LibraryFiles.OuputLibraryFile(
				Weazel.Badger.Vhdl.LibraryFiles.Libraries.StdLogicArithExt, 
				configuration.HardwareImplementationDirectory
			);

			// write 'user_logic' entity
			userLogicDesignFile.Write(configuration.HardwareImplementationDirectory);

			// create a list of files to put into the ISE project
			List<string> files = new List<string>();

			// write entities
			writeEntityToFile(ref files, hardwareEntity, configuration.HardwareImplementationDirectory);

			// add queues
			files.AddRange(queueRegistry.GetFileNames());

			// add top level entity
			files.Add("weazel.vhd");

			// add the special 'std_logic_arithext' file used by some gezel generated vhdl entities
			files.Add(Weazel.Badger.Vhdl.LibraryFiles.LibraryFiles.GetLibraryFileName(
				Weazel.Badger.Vhdl.LibraryFiles.Libraries.StdLogicArithExt));
				
			// add the 'user_logic' file
			files.Add(userLogicDesignFile.FileName);
			
			Weazel.Badger.Xilinx.IseProject project = new Weazel.Badger.Xilinx.IseProject();

			// set some project options ..
			project.ProjectName = "badger_output";
			project.DevicePackage = configuration.Device.Package;
			project.Device = configuration.Device.Name;
			project.DeviceSpeed = configuration.Device.Speed;
			project.DeviceFamily = configuration.Device.Family;

			Weazel.Badger.Xilinx.IseProject.LibraryFileCollection userFilesLibrary =
				new Weazel.Badger.Xilinx.IseProject.LibraryFileCollection(
					"weazel_v1_00_a", configuration.HardwareImplementationDirectory + "//", files);

			project.AddLibraryFiles(userFilesLibrary);

			// add needed ipif libraries
			project.AddLibraryFiles(new ProcCommonV200aLibraryFiles(configuration.XilinxProcessorIpLibrary));
			project.AddLibraryFiles(new InterruptControl100aLibraryFiles(configuration.XilinxProcessorIpLibrary));
			project.AddLibraryFiles(new WrpFifov101bLibraryFiles(configuration.XilinxProcessorIpLibrary));
			project.AddLibraryFiles(new Rdpfifov101bLibraryFiles(configuration.XilinxProcessorIpLibrary));
			project.AddLibraryFiles(new OpbIpifv301aLibraryFiles(configuration.XilinxProcessorIpLibrary));

			// write to disk
			project.WriteToFile(configuration.IseProjectDirectory + "//project.npl");
		}



		public IpifImplementation(Model.Model model, Weazel.Badger.ConversionEntity hardwareEntity)
		{
			this.hardwareEntity = hardwareEntity;
			this.model = model;
		}
	}
}
